/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022.
 * Description: hkids report msg interface.
 * Author: luohaizheng
 * Create: 2021-05-15
 */

#include <linux/slab.h>
#include <linux/string.h>
#include <linux/audit.h>
#include <linux/atomic.h>
#include <linux/timekeeping.h>
#include <linux/hkids/hkids.h>
#include <linux/kbox/kbox.h>

#define MSG_LEN 4096
#define HKIDS_DISABLED (-1)
#define HKIDS_MAX_LOG_SIZE 0x400000

static int g_hkids_reg = HKIDS_DISABLED;
static atomic_t log_seq = ATOMIC_INIT(0);

unsigned int hkids_get_log_seq(void)
{
	return (unsigned int)atomic_read(&log_seq);
}
EXPORT_SYMBOL_NS(hkids_get_log_seq, HW_RTOS_NS);

static inline unsigned int hkids_log_seq_inc(void)
{
	return (unsigned int)atomic_inc_return(&log_seq);
}

int hkids_create_kbox_reg(size_t size)
{
	int reg;
	struct kbox_region region = {
		.name = "hkids",
		.size = size,
	};

	if (size == 0 || size > HKIDS_MAX_LOG_SIZE || !PAGE_ALIGNED(size))
		return -EINVAL;

	reg = kbox_register_region(region);
	if (reg < 0)
		return reg;

	g_hkids_reg = reg;
	return 0;
}
EXPORT_SYMBOL_NS(hkids_create_kbox_reg, HW_RTOS_NS);

static void notrace hkids_report_kbox(const char *fmt, va_list args)
{
	struct timespec64 ts;
	struct tm tm;
	int header_len;
	int content_len;
	char *msg = NULL;

	if (g_hkids_reg == HKIDS_DISABLED)
		return;

	msg = kzalloc(MSG_LEN, GFP_ATOMIC);
	if (msg) {
		ktime_get_real_ts64(&ts);
		time64_to_tm(ts.tv_sec, 0, &tm);
		/* this scnprintf never fail */
		header_len = scnprintf(msg, MSG_LEN, "time=%04ld%02d%02d%02d%02d%02d id=%u ",
				tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour,
				tm.tm_min, tm.tm_sec,
				hkids_log_seq_inc());
		if (header_len <= 0) {
			kfree(msg);
			return;
		}

		content_len = vscnprintf(msg + header_len, MSG_LEN - header_len, fmt, args);
		if (content_len > 0)
			kbox_write(g_hkids_reg, msg, header_len + content_len);
		kfree(msg);
	} else {
		pr_warn("[HKIDS] failed to log with hkids kbox, report to dmesg\n");
		vprintk(fmt, args);
		pr_info("\n");
	}
}

void notrace hkids_report(const char *fmt, ...)
{
	va_list args;

	va_start(args, fmt);
	hkids_report_kbox(fmt, args);
	va_end(args);
}
EXPORT_SYMBOL_NS(hkids_report, HW_RTOS_NS);
